/*
 * TurnServer, the OpenSource Java Solution for TURN protocol. Maintained by the
 * Jitsi community (http://jitsi.org).
 *
 * Copyright @ 2015 Atlassian Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jitsi.turnserver.stack;

import java.util.Arrays;
import java.util.logging.*  ;

import org.ice4j.*;
import org.ice4j.message.*;
import org.ice4j.stack.*;

/**
 * Class to handle incoming ChannelData messages coming from Client to Server.
 * It first finds if there is a ChannelBind installed for the peer. 
 * If yes it then sends the UDP message to peer.
 * If no it then silently ignores the message.
 * 
 * @author Aakash Garg
 */
public class ServerChannelDataEventHandler implements
	ChannelDataEventHandler {
    
    /**
     * The <tt>Logger</tt> used by the
     * <tt>ServerChannelDataEventHandler</tt> class and its instances for
     * logging output.
     */
    private static final Logger logger = Logger
        .getLogger(ServerChannelDataEventHandler.class.getName());

    /**
     * The turnStack to call.
     */
    private TurnStack turnStack;

    /**
     * Default Constructor.
     */
    public ServerChannelDataEventHandler()
    {
    }
    
    /**
     * Parametrized constructor.
     * @param turnStack the turnStack to set for this class.
     */
    public ServerChannelDataEventHandler(StunStack turnStack) 
    {
	if (turnStack instanceof TurnStack)
        {
            this.turnStack = (TurnStack) turnStack;
        }
        else
        {
            throw new IllegalArgumentException("This is not a TurnStack!");
        }
    }

    /**
     * Sets the TurnStack for this class.
     * @param turnStack the turnStack to set for this class.
     */
    public void setTurnStack(TurnStack turnStack)
    {
	this.turnStack = turnStack;
    }
    
    /**
     * Handles the ChannelDataMessageEvent.
     * @param evt the ChannelDataMessageEvent to handle/process.
     */
    @Override
    public void handleMessageEvent(ChannelDataMessageEvent evt) 
    {
        if(!logger.isLoggable(Level.FINER)){
            logger.setLevel(Level.FINER);
        }
	ChannelData channelData = evt.getChannelDataMessage();
	char channelNo = channelData.getChannelNumber();
	byte[] data = channelData.getData();
	logger.finer("Received a ChannelData message for " + (int)channelNo
		+ " , message : " + Arrays.toString(data));
	
	TransportAddress clientAddress = evt.getRemoteAddress();
        TransportAddress serverAddress = evt.getLocalAddress();
        Transport transport = Transport.UDP;
        FiveTuple fiveTuple =
            new FiveTuple(clientAddress, serverAddress, transport);
        
        Allocation allocation 
            = this.turnStack.getServerAllocation(fiveTuple);
        
        if(allocation==null)
        {
            logger.finer("allocation not found.");
        }
        else if(!allocation.containsChannel(channelNo))
        {
	    logger.finer("ChannelNo " + (int) channelNo
		    + " not found in Allocation!");
            return;
        }
        TransportAddress destAddr = allocation.getPeerAddr(channelNo);
        if(destAddr != null)
        {
	    RawMessage message = RawMessage.build(data, data.length, destAddr,
		    allocation.getClientAddress());
	    try {
		logger.finer("Dispatching a UDP message to " + destAddr
			+ ", data: " + Arrays.toString(message.getBytes()));
		this.turnStack.sendUdpMessage(message, destAddr,
		    allocation.getRelayAddress());
	    } catch (StunException e) {
		logger.finer(e.getMessage());
	    }
        }
        else
        {
	    logger.finer("Peer address not found for channel "
		    + (int) channelNo);
        }

    }

}
